﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using LibCommon;
using LibRTC.Core.MessageMap;
using LibRTC.Interface;
using RTCCore;

namespace LibRTC.Core
{
    public class RTCCoreClient : IRTCCoreOperations
    {
        // i want to make it a singleton
        private static RTCCoreClient _instance = null;
        private static readonly RTCSystem _rtcSystem = RTCSystem.GetInstance();

        private static ILogger LOGGER = LoggerFactory.GetLogger("RTCCoreClient", null);

        #region Fields

        private RTCClientClass _client;
        private IRTCProfile2 _profile;
        private bool _presenceEnabled;
        private RTC_REGISTRATION_STATE _registrationState = RTC_REGISTRATION_STATE.RTCRS_NOT_REGISTERED;
        private RTC_PRESENCE_STATUS _presenceStatus = RTC_PRESENCE_STATUS.RTCXS_PRESENCE_OFFLINE;
        
        #endregion Fields


        #region Attributues

        /// <summary>
        /// Profile, keep user infomation
        /// </summary>
        public IRTCProfile2 Profile
        {
            get
            {
                return _profile;
            }
            set
            {
                _profile = value;
            }

        }

        /// <summary>
        /// Client Object
        /// </summary>
        public RTCClientClass RTCClient
        {
            get
            {
                return _client;
            }
            set
            {
                _client = value;
            }
        }

        public RTC_REGISTRATION_STATE RegistrationState
        {
            get
            {
                return _registrationState;
            }
            set
            {
                _registrationState = value;
            }
        }

        public RTC_PRESENCE_STATUS PresenceStatus
        {
            get
            {
                return _presenceStatus;
            }
        }

        /// <summary>
        /// Presence Interface, would be decomissioned later
        /// </summary>
        public IPresenceUI IPresenceUI
        {
            get
            {
                return _presenceUI;
            }
        }

        public ILogOnUI ILogOnUI
        {
            get
            {
                return _logOnUI;
            }
        }

        #endregion Attributes


        #region Interfaces
        // user defined Interfaces

        // LogOn interface
        private ILogOnUI _logOnUI;
        // presence interface
        private IPresenceUI _presenceUI;

        /// <summary>
        /// Set LogOn UI
        /// </summary>
        /// <param name="logOnUI"></param>
        public void SetLogOnUI(ILogOnUI logOnUI)
        {
            this._logOnUI = logOnUI;
        }

        /// <summary>
        /// Set PresenceUI
        /// </summary>
        /// <param name="presenceUI"></param>
        public void SetIPresenceUI(IPresenceUI presenceUI)
        {
            this._presenceUI = presenceUI;
        }

        #endregion Interfaces


        /// <summary>
        /// initialize the RTC COM Object
        /// </summary>
        public RTCCoreClient()
        {
            LOGGER.LogDebug("Entering RTCPresenceCore.RTCPresenceCore");

            _instance = this;

            _presenceEnabled = false;

            _registrationState = RTC_REGISTRATION_STATE.RTCRS_NOT_REGISTERED;

            try
            {
                _client = new RTCClientClass();

                _client.Initialize();

                int mediaTypes = RTCConst.RTCMT_ALL;
                //int mediaTypes =  RTCConst.RTCMT_AUDIO_RECEIVE | RTCConst.RTCMT_AUDIO_SEND; 

                _client.SetPreferredMediaTypes(mediaTypes, true);
                //this._client.PreferredAEC = true;

                // Set the event filter
                _client.EventFilter = RTCConst.RTCEF_ALL;

                _client.IRTCEventNotification_Event_Event += new IRTCEventNotification_EventEventHandler(OnRTCEvent);
                _client.ListenForIncomingSessions = RTC_LISTEN_MODE.RTCLM_DYNAMIC;

                _client.ClientName = "VCEDU";
                _client.ClientCurVer = "1.0";

                // Don't Initialize presence status -- exception if called
                // RTC_E_PRESENCE_NOT_ENABLED 0x80EE0028L
                //DoSetPresence(RTC_PRESENCE_STATUS.RTCXS_PRESENCE_ONLINE);
            }
            catch (COMException e1)
            {
                LOGGER.LogError(RtcClientErrorTranslator.ToString(e1.ErrorCode));

                throw;
            }            
        }

        /// <summary>
        /// Destructor , used to release resources
        /// </summary>
        ~RTCCoreClient()
        {

        }

        /// <summary>
        /// Get an instance of RTCCoreClient
        /// I want to make it a singleton
        /// </summary>
        /// <returns>instance of RTCCoreClient</returns>
        public static RTCCoreClient GetInstance()
        {
            if(null == _instance)
            {
                _instance = new RTCCoreClient();
            }

            return _instance;
        }

        /// <summary>
        /// Event Processing Method
        /// </summary>
        /// <param name="rtcEventType">event type</param>
        /// <param name="rtcEvent">event object</param>
        virtual public void OnRTCEvent(RTC_EVENT rtcEventType, object rtcEvent)
        {
            RTCMessageMap.GetInstance().OnRTCEvent(rtcEventType, rtcEvent);
        }

        /// <summary>
        /// Shutdown the whole system
        /// </summary>
        /// <returns>boolean value to indicate whether the operation is successful</returns>
        public bool DoShutdown()
        {
            LOGGER.LogDebug("Entering RTCPresenceCore.DoShutdown");

            if (_client != null)
            {
                DoLogOff();
                _client.PrepareForShutdown();

                //the UI should wait for shutdown
                return true;
            }

            return false;
        }

        /// <summary>
        /// Do LogOn, set the presence info 
        /// </summary>
        /// <param name="username">username</param>
        /// <param name="password">password</param>
        public void DoLogOn(string username,string password, RTC_PRESENCE_STATUS presenceStatus)
        {
            Debug.Assert(null == _profile);
            Debug.Assert(null != username);
            Debug.Assert(null != password);

            if (null != _profile)
            {
                DoLogOff();
                LOGGER.LogWarning("Already logged on, would disable it!");
            }

            try
            {
                _rtcSystem.LocalUserName = username;
                _rtcSystem.LocalUserPassword = password;

                _client.LocalUserName = _rtcSystem.LocalUserName;
                _client.LocalUserURI = _rtcSystem.LocalUserURI;

                string xml = "<provision key=\"" + _rtcSystem.LocalUserName + "\" name=\"" + _rtcSystem.LocalUserName + "\">" +
                             "<user uri=\"" + _rtcSystem.LocalUserURI + "\"  account=\"" + _rtcSystem.LocalUserName + "\" password=\"" + _rtcSystem.LocalUserPassword + "\"  />" +
                             "<sipsrv addr=\""+_rtcSystem.GetSipServerIp()+"\" protocol=\"UDP\" role=\"proxy\" auth=\"digest\">      "  +
                             "<session party=\"first\" type=\"pc2pc\" />                            "  +
                             " <session party=\"first\" type=\"im\" />                              "  +
                             "</sipsrv> "                                                             +
                             " <sipsrv addr=\"" + _rtcSystem.GetSipServerIp() + "\" protocol=\""+_rtcSystem.GetSipTransportString()+"\" role=\"registrar\" auth=\"digest\"/>" +
                             "</provision>";

                Profile = (IRTCProfile2)_client.CreateProfile(xml);

                Debug.Assert(null != Profile);

                // set presence status
                _presenceStatus = presenceStatus;

                DoRegister();
            }
            catch (COMException e1)
            {
                LOGGER.LogError(RtcClientErrorTranslator.ToString(e1.ErrorCode));

                _presenceUI.OnLoggedOff();
            }
        }

        public void DoLogOff()
        {
            LOGGER.LogDebug("Entering RTCPresenceCore.DoLogOff");
            
            DoDisablePresence();
            _presenceUI.OnLoggedOff();

            if (null == Profile)
            {
                return;
            }

            Debug.Assert(null != Profile);

            _presenceStatus = RTC_PRESENCE_STATUS.RTCXS_PRESENCE_OFFLINE;
            _client.DisableProfile(Profile);
            Profile = null;
        }

        /// <summary>
        /// Set the presence info
        /// </summary>
        /// <param name="presenceStatus">presence status</param>
        /// <param name="notes">note string</param>
        public void DoSetPresence(RTC_PRESENCE_STATUS presenceStatus, string notes)
        {
            LOGGER.LogDebug("Entering RTCPresenceCore.DoSetPresence");
            _client.SetLocalPresenceInfo(presenceStatus, notes);
        }

        /// <summary>
        /// Disable the Presence, it should be used before logoff
        /// </summary>
        public void DoDisablePresence()
        {
            LOGGER.LogDebug("Entering RTCPresenceCore.DoDisablePresence");

            _presenceUI.ClearBuddyList();

            if (_presenceEnabled == false)
            {
                return;
            }

            _client.DisablePresence();
            _presenceEnabled = false;
        }

        /// <summary>
        /// Enable the presence info
        /// </summary>
        private void DoEnablePresence()
        {
            LOGGER.LogDebug("Entering RTCPresenceCore.DoEnablePresence");

            _presenceUI.ClearBuddyList();

            if (_presenceEnabled == true)
            {
                return;
            }

            // Build the filename for presence storage
            // Replace all non-alphanumeric characters
            // in the URI with underscore
            string uri = Regex.Replace(_profile.UserURI, @"\W", "_");
            StringBuilder sb = new StringBuilder();
            sb.Append("rtcpresence_").Append(uri).Append(".xml");

            // Enable presence
            _client.EnablePresenceEx(_profile, sb.ToString(), 0);

            // Set a presence property
            string name = "http://schemas.microsoft.com/rtc/rtcpresence";
            string val = "<name> rtcpresence </rtcpresence>";
            _client.SetPresenceData(name, val);

            string deviceName = System.Environment.MachineName + " (RTCPresence)";
            _client.set_PresenceProperty(RTC_PRESENCE_PROPERTY.RTCPP_DEVICE_NAME, deviceName);

            LOGGER.LogDebug("Enable Presence file=" + sb.ToString() + " device=" + deviceName);

            _client.OfferWatcherMode = RTC_OFFER_WATCHER_MODE.RTCOWM_AUTOMATICALLY_ADD_WATCHER;
            _presenceEnabled = true;

            // set local presence info
            _client.SetLocalPresenceInfo(_presenceStatus, null);
        }

        /// <summary>
        /// Register username/password to a sip server
        /// </summary>
        public void DoRegister()
        {
            Trace.WriteLine("Entering RTCPresenceCore.DoRegister");

            this.DoEnablePresence();

            LOGGER.LogDebug(_profile.XML);

            try
            {
                _client.EnableProfileEx(_profile, RTCConst.RTCRF_REGISTER_ALL, 0);
                //_client.EnableProfile(_profile,RTCConst.RTCRF_REGISTER_ALL);
               // set local presence info
               _client.SetLocalPresenceInfo(_presenceStatus, "");
            }
            catch (COMException e1)
            {
                
                LOGGER.LogError(RtcClientErrorTranslator.ToString(e1.ErrorCode));
            }

        }

        /// <summary>
        /// start a call to participant
        /// </summary>
        /// <param name="remoteUserURI">remote user(SIP URI)</param>
        /// <param name="type">session type</param>
        /// <returns>return a  new session , or null if fail</returns>
        public IRTCSession MakeCall(string remoteUserURI,RTC_SESSION_TYPE type)
        {
            IRTCSession session = null;
            try
            {
                // create a session
                session = _client.CreateSession(type, null, _profile, 0);
                Debug.Assert(null != session);

                // add user to participant
                session.AddParticipant(remoteUserURI, remoteUserURI);

            }
            catch (COMException e1)
            {
                LOGGER.LogError(RtcClientErrorTranslator.ToString(e1.ErrorCode));
                throw;
            }

            return session;
        }

        /// <summary>
        /// Send a message to remoteUserURI
        /// </summary>
        /// <param name="remoteUserURI">remote user</param>
        /// <param name="messageHeader">message header</param>
        /// <param name="message">message body</param>
        /// <param name="lCookie">cookie</param>
        public void SendMessage(string remoteUserURI, string messageHeader, string message, int lCookie)
        {
            Debug.Assert(null != remoteUserURI);
            
            IRTCSession session = MakeCall(remoteUserURI,RTC_SESSION_TYPE.RTCST_IM);

            Debug.Assert(null != session);

            session.SendMessage(messageHeader, message, lCookie);

            // the session will automatically closed

        }

        /// <summary>
        /// return client object
        /// </summary>
        /// <returns>client object</returns>
        public RTCClientClass GetRTCClientClass()
        {
            return _client;
        }


        public void SubscribeBuddyStatus(string userURI, string userName)
        {
            Debug.Assert(null != userURI);
            Debug.Assert(null != userName);

            Debug.Assert(null != _client);

            try {
                _client.AddBuddy(userURI, userName, null, true, _profile, RTCConst.RTCCS_FORCE_PROFILE);
            }
            catch (COMException e) {
                LOGGER.LogError(RtcClientErrorTranslator.ToString(e.ErrorCode)+" userURI:"+userURI);    
            }

        }

        public void AddNotifyWatcher(string userURI, string userName)
        {
            Debug.Assert(null != userURI);
            Debug.Assert(null != userName);

            try {
                _client.AddWatcher(userURI, userName, null, false, true);
            }
            catch (COMException e) {
                LOGGER.LogError(RtcClientErrorTranslator.ToString(e.ErrorCode) + " userURI:" + userURI);   
            }


        }
    }
}